package ivyeclipse;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.ivy.ant.IvyCacheTask;
import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.core.report.ArtifactDownloadReport;
import org.apache.tools.ant.BuildException;

public class IvyEclipse extends IvyCacheTask {

    private String dir =".";
    public String getDir() { return dir; }
    public void setDir(String dir) { this.dir = dir; }

    private Set<String> excludeSet = new HashSet<String>();
    private String exclude="";
    public String getExclude() { return exclude; }
    public void setExclude(String exclude) {
        this.exclude = exclude;
        for (String module : exclude.split("\\s")) {
            excludeSet.add(module);
        }
    }

    @Override
    public void doExecute() throws BuildException {
        prepareAndCheck();
        try {
            List paths = getArtifactReports();
            Set<ArtifactDownloadReport> jars = buildJarsList(paths);
            Map<ArtifactDownloadReport, ArtifactDownloadReport> sources = buildArtifactsMap("source", jars, paths);
            Map<ArtifactDownloadReport, ArtifactDownloadReport> javadocs = buildArtifactsMap("javadoc", jars, paths);
            StringBuffer s = createClasspathXml(jars, sources, javadocs);
            writeClasspathFile(s);
        } catch (Exception e) {
            throw new BuildException("impossible to build eclipse classpath: " + e, e);
        }
    }

    private Set<ArtifactDownloadReport> buildJarsList(List paths) {
        Set<ArtifactDownloadReport> jars = new LinkedHashSet();
        for (Iterator iter = paths.iterator(); iter.hasNext();) {
            ArtifactDownloadReport a = (ArtifactDownloadReport) iter.next();
            if (a.getType().equals("jar") && !excludeSet.contains(a.getArtifact().getModuleRevisionId().getName())) {
                jars.add(a);
            }
        }
        return jars;
    }

    private Map<ArtifactDownloadReport, ArtifactDownloadReport> buildArtifactsMap(String type, Set<ArtifactDownloadReport> jars, List paths) {
        Map<ArtifactDownloadReport, ArtifactDownloadReport> m = new HashMap<ArtifactDownloadReport, ArtifactDownloadReport>();
        for (Iterator iter = paths.iterator(); iter.hasNext();) {
            ArtifactDownloadReport a = (ArtifactDownloadReport) iter.next();
            if (a.getType().equals(type)) {
                ArtifactDownloadReport jar = getJarArtifactForSourceArtifact(jars, a.getArtifact());
                if (jar!=null) {
                    m.put(jar, a);
                }
            }
        }
        return m;
    }
    
    private ArtifactDownloadReport getJarArtifactForSourceArtifact(Set<ArtifactDownloadReport> jars, Artifact a) {
        for (ArtifactDownloadReport j : jars) {
            Artifact ja = j.getArtifact();
            if (ja.getName().equals(a.getName()) &&
                ja.getModuleRevisionId().getOrganisation().equals(a.getModuleRevisionId().getOrganisation()) &&
                ja.getModuleRevisionId().getName().equals(a.getModuleRevisionId().getName()) &&
                ja.getModuleRevisionId().getRevision().equals(a.getModuleRevisionId().getRevision())) {
                return j;
            }
        }
        return null;
    }

    private StringBuffer createClasspathXml(Set<ArtifactDownloadReport> jars, Map<ArtifactDownloadReport, ArtifactDownloadReport> sources, Map<ArtifactDownloadReport, ArtifactDownloadReport> javadocs) {
        StringBuffer s = new StringBuffer();
        for (ArtifactDownloadReport jar : jars) {
            ArtifactDownloadReport source = sources.get(jar);
            ArtifactDownloadReport javadoc = javadocs.get(jar);
            String srcAttr = source==null ? "" : " sourcepath=\"" + fixPathForEclipseXml(source.getLocalFile().getAbsolutePath()) + "\"";
            String javadocAttr = javadoc==null ? "" : "\t\t<attributes><attribute name=\"javadoc_location\" value=\"jar:file:/" + fixPathForEclipseXml(javadoc.getLocalFile().getAbsolutePath()) + "!/\"/></attributes>";
            s.append("\t<classpathentry kind=\"lib\" path=\"" + fixPathForEclipseXml(jar.getLocalFile().getAbsolutePath()) + "\"" + srcAttr + (javadocAttr.equals("") ? "/>\n" : ">\n" + javadocAttr + "\n\t</classpathentry>\n"));
        }
        if (s.length()!=0) {
            s.deleteCharAt(s.length()-1);
        }
        return s;
    }

    private String fixPathForEclipseXml(String path) {
        return path.replace('\\', '/');
    }

    private void writeClasspathFile(StringBuffer s) throws IOException {
        String template = readFile(dir +"/.classpath-template");
        String text = template.replaceAll("@@CLASSPATH@@", s.toString());
        writeFile(dir + "/.classpath", text);
    }

    private void writeFile(String fileName, String text) throws IOException {
        File f = new File(getProject().getBaseDir() + File.separator + fileName);
        Writer output = new BufferedWriter(new FileWriter(f));
        try {
            output.write(text);
        }
        finally {
            output.close();
        }
    }

    private String readFile(String fileName) throws IOException {
        File f = new File(getProject().getBaseDir() + File.separator + fileName);
        BufferedReader input = new BufferedReader(new FileReader(f));
        String line;
        StringBuffer contents = new StringBuffer();
        try {
            while ((line = input.readLine()) != null) {
                contents.append(line);
                contents.append(System.getProperty("line.separator"));
            }
            return contents.toString();
        } finally {
            input.close();
        }
    }



}